package utils;


import annotion.Column;
import annotion.Id;
import annotion.Relation;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description: 泛型Dao通用方法
 * @Author one world
 * @Date 2020/9/20 0020 17:11
 */
public class GenicaDaoImpl<T, PK extends Serializable> implements GenicaDao<T, PK> {
    private Class<T> tClazz = null;//实体Class
    private Class<PK> pkClazz = null;//主键Class
    String tableName = null;//表名
    String pk = null;//注解中的主键名
    private static final Logger logger = LoggerFactory.getLogger(GenicaDaoImpl.class);

    /**
     * 构造方法初始化属性
     */
    public GenicaDaoImpl() {
        tClazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        pkClazz = (Class<PK>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
        tableName = DaoUtil.getTable(tClazz);
        pk = DaoUtil.getPK(tClazz);
    }

    /**
     * 分页查询所有
     *
     * @param currentPage
     * @param limit
     * @return
     */
    @Override
    public List<T> findAll(Integer currentPage, Integer limit) {
        currentPage = (currentPage - 1) * limit;
        String sql = "select * from " + tableName + " limit ?,?";
        return DaoUtil.queryList(tClazz, sql, currentPage, limit);
    }

    /**
     * 查询所有
     *
     * @return
     */
    @Override
    public List<T> findAll() {
        String sql = "select * from " + tableName;
        return DaoUtil.queryList(tClazz, sql);
    }

    /**
     * 根据主键查询实体
     *
     * @param id
     * @return true/false
     */
    @Override
    public T findByPK(PK id) {
        T t = null;
        String sql = "select * from " + tableName + " where " + pk + "=?";
        t = DaoUtil.query(tClazz, sql, id);
        return t;
    }

    /**
     * 根据主键删除
     *
     * @param id
     * @return true/false
     */
    @Override
    public boolean delByPK(PK id) {
        String sql = "delete from " + tableName + " where " + pk + "=?";
        return DaoUtil.update(sql, id);
    }

    /**
     * 向数据库添加
     *
     * @param t
     * @return true/false
     */
    @Override
    public boolean add(T t) {
        String sql = "insert into " + tableName + "(";
        StringBuffer sb = new StringBuffer(sql);
        Map map = DaoUtil.getNameAndValue(tClazz);
        Object[] params = new Object[tClazz.getDeclaredFields().length - 1];
        String id = DaoUtil.getPK(tClazz);
        int index = 0;
        try {
            for (Object key : map.keySet()) {
                String methodName = "get" + getName(map.get(key).toString());
                Method method = tClazz.getDeclaredMethod(methodName);
                Field field = tClazz.getDeclaredField(map.get(key).toString());
                if (id != key&&!field.isAnnotationPresent(Relation.class)) {
                    params[index] = method.invoke(t);
                    sb.append(key.toString() + ",");
                    index++;
                }
                if(field.isAnnotationPresent(Relation.class)){
                    Field[] rfields = field.getType().getDeclaredFields();
                    for(Field rfield: rfields){
                        if (rfield.isAnnotationPresent(Id.class)){
                            rfield.setAccessible(true);
                            String rMethodName = "get"+getName(rfield.getName());
                            Method rMethod = field.getType().getDeclaredMethod(rMethodName);
                            params[index]=rMethod.invoke(method.invoke(t));
                            index++;
                            break;
                        }
                    }
                    sb.append(key.toString() + ",");
                }
            }
            sb = new StringBuffer(sb.substring(0,sb.length()-1));
            sb.append(") values(");
            for(int i=0;i<index;i++){
                if(i+1!=index){
                    sb.append("?,");
                }else {
                    sb.append("?)");
                }
            }
            return DaoUtil.update(sb.toString(), params);
        } catch (NoSuchMethodException | IllegalAccessException |InvocationTargetException |NoSuchFieldException e) {
            logger.error("GenicaDaoImpl类add方法异常",e);
        }
        return false;
    }

    /**
     * 更新指定记录
     *
     * @param t
     * @return
     */
    @Override
    public boolean update(T t) {
        StringBuffer sb = new StringBuffer("update " + tableName + " set ");
        Field[] fields = tClazz.getDeclaredFields();
        //用于存放参数，存放完毕后直接传入DaoUtil工具类进行操作
        Object[] params = new Object[fields.length + 1];
        //标记，用于标记字段是否为id字段
        boolean flag = false;
        //主键字段
        Object id = null;
        try {
            for (int i = 0; i < fields.length; i++) {
                fields[i].setAccessible(true);
                String methodName = "get" + getName(fields[i].getName());
                Method method = tClazz.getDeclaredMethod(methodName);
                if (fields[i].isAnnotationPresent(Id.class)) {
                    String pk = fields[i].getAnnotation(Id.class).value();
                    if (pk == null || pk.equals("")) {
                        sb.append(fields[i].getName() + "=?");
                    } else {
                        sb.append(pk + "=?");
                    }
                    flag = true;
                    params[i] = method.invoke(t);
                } else if (fields[i].isAnnotationPresent(Column.class)) {
                    sb.append(fields[i].getAnnotation(Column.class).value() + "=?");
                    params[i] = method.invoke(t);
                } else if (fields[i].isAnnotationPresent(Relation.class)) {
                    Object fk = null;
                    sb.append(fields[i].getAnnotation(Relation.class).value() + "=?");
                    Field[] rfields = fields[i].getType().getDeclaredFields();
                    for (Field rfield : rfields) {
                        if (rfield.isAnnotationPresent(Id.class)) {
                            rfield.setAccessible(true);
                            String rMethodName = "get" + getName(rfield.getName());
                            Method rMethod = fields[i].getType().getDeclaredMethod(rMethodName);
                            fk = rMethod.invoke(method.invoke(t));
                            System.out.println(fk);
                            break;
                        }
                    }
                    params[i] = fk;
                } else {
                    sb.append(fields[i].getName() + "=?");
                    params[i] = method.invoke(t);
                }
                if (i + 1 != fields.length) {
                    sb.append(",");
                }
                if (flag) {
                    params[fields.length] = params[i];
                    //清除标记，防止字段读取异常
                    flag = false;
                }
            }
            sb.append(" where " + pk + "=?");
            return DaoUtil.update(sb.toString(), params);
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            logger.error("GenicaDaoImpl类update方法异常",e);
        }
        return false;
    }

    /**
     * 将字段名首字母大写，返回拼接完整的方法名，反射调用方法
     *
     * @param name
     * @return
     */
    public static String getName(String name) {
        name = name.substring(0, 1).toUpperCase() + name.substring(1);
        return name;
    }

}
